/**
 * 碰撞检测工具类
 * 参考 Mapbox GL JS 的碰撞检测实现
 * 用于POI标签的遮挡检测和自动隐藏
 */

import * as Cesium from 'cesium'

class CollisionGrid {
  constructor(width, height, cellSize = 25) {
    this.width = width
    this.height = height
    this.cellSize = cellSize
    this.cols = Math.ceil(width / cellSize)
    this.rows = Math.ceil(height / cellSize)
    this.grid = new Array(this.cols * this.rows).fill(null).map(() => [])
  }

  // 获取网格索引
  getCellIndex (x, y) {
    const col = Math.floor(x / this.cellSize)
    const row = Math.floor(y / this.cellSize)
    if (col < 0 || col >= this.cols || row < 0 || row >= this.rows) {
      return -1
    }
    return row * this.cols + col
  }

  // 插入碰撞盒
  insert (box, data) {
    const { x1, y1, x2, y2 } = box
    const startCol = Math.max(0, Math.floor(x1 / this.cellSize))
    const endCol = Math.min(this.cols - 1, Math.floor(x2 / this.cellSize))
    const startRow = Math.max(0, Math.floor(y1 / this.cellSize))
    const endRow = Math.min(this.rows - 1, Math.floor(y2 / this.cellSize))

    for (let row = startRow; row <= endRow; row++) {
      for (let col = startCol; col <= endCol; col++) {
        const index = row * this.cols + col
        this.grid[index].push({ box, data })
      }
    }
  }

  // 检测碰撞
  hitTest (testBox, excludeData = null) {
    const { x1, y1, x2, y2 } = testBox
    const startCol = Math.max(0, Math.floor(x1 / this.cellSize))
    const endCol = Math.min(this.cols - 1, Math.floor(x2 / this.cellSize))
    const startRow = Math.max(0, Math.floor(y1 / this.cellSize))
    const endRow = Math.min(this.rows - 1, Math.floor(y2 / this.cellSize))

    for (let row = startRow; row <= endRow; row++) {
      for (let col = startCol; col <= endCol; col++) {
        const index = row * this.cols + col
        const cell = this.grid[index]

        for (const item of cell) {
          if (excludeData && item.data === excludeData) continue

          // 检查两个矩形是否重叠
          if (this.boxesIntersect(testBox, item.box)) {
            return true
          }
        }
      }
    }
    return false
  }

  // 检查两个矩形是否重叠
  boxesIntersect (box1, box2) {
    return !(box1.x2 < box2.x1 ||
      box2.x2 < box1.x1 ||
      box1.y2 < box2.y1 ||
      box2.y2 < box1.y1)
  }

  // 清空网格
  clear () {
    this.grid.forEach(cell => cell.length = 0)
  }
}

class POICollisionDetector {
  constructor(viewer) {
    this.viewer = viewer
    this.viewportPadding = 100
    this.grid = null
    this.placedLabels = new Map() // 存储已放置的标签信息
    this.labelPriorities = {
      'place': 1,      // 地名最高优先级
      'poi': 2,        // POI次之
      'road': 3,       // 道路标签
      'water': 4,      // 水体标签
      'default': 5     // 默认最低
    }
  }

  // 初始化碰撞网格
  initializeGrid () {
    const canvas = this.viewer.scene.canvas
    const width = canvas.clientWidth + 2 * this.viewportPadding
    const height = canvas.clientHeight + 2 * this.viewportPadding
    // 使用适中的网格单元平衡性能和效果
    this.grid = new CollisionGrid(width, height, 40)
    this.placedLabels.clear()
  }

  // 获取标签的屏幕坐标边界盒
  getLabelBoundingBox (entity) {
    if (!entity.position || !entity.label) return null

    try {
      // 验证位置是否有效
      let position = entity.position
      if (typeof position.getValue === 'function') {
        // 使用当前时间获取Property的值
        const currentTime = this.viewer.clock.currentTime
        position = position.getValue(currentTime)
      }

      // 基本验证：确保position存在
      if (!position) {
        return null
      }

      // 如果是Cartesian3对象，检查坐标
      if (position.x !== undefined && position.y !== undefined && position.z !== undefined) {
        if (!isFinite(position.x) || !isFinite(position.y) || !isFinite(position.z)) {
          return null
        }
      }

      // 将世界坐标转换为屏幕坐标
      const screenPosition = Cesium.SceneTransforms.worldToWindowCoordinates(
        this.viewer.scene,
        position
      )

      if (!screenPosition) return null

      // 验证屏幕坐标
      if (!isFinite(screenPosition.x) || !isFinite(screenPosition.y)) {
        return null
      }

      // 估算标签尺寸
      const label = entity.label
      const currentTime = this.viewer.clock.currentTime

      const text = (label.text?.getValue ? label.text.getValue(currentTime) : label.text) || ''
      if (!text || text.length === 0) return null

      const fontSize = this.parseFontSize((label.font?.getValue ? label.font.getValue(currentTime) : label.font) || '12px Arial')
      const scale = (label.scale?.getValue ? label.scale.getValue(currentTime) : label.scale) || 1

      // 验证数值
      if (!isFinite(fontSize) || !isFinite(scale) || fontSize <= 0 || scale <= 0) {
        return null
      }

      const actualFontSize = fontSize * scale

      // 粗略估算文字宽度 (更精确的方法需要canvas测量)
      const charWidth = actualFontSize * 0.5 // 减小字符宽度估算，让标签更紧凑
      const textWidth = Math.min(text.length * charWidth, 400) // 减小最大宽度
      const textHeight = actualFontSize * 1.1 // 减小行高

      // 考虑描边宽度
      const outlineWidth = (label.outlineWidth?.getValue ? label.outlineWidth.getValue(currentTime) : label.outlineWidth) || 0
      const padding = Math.max(0, outlineWidth) + 1 // 减小padding

      // 计算边界盒
      const pixelOffset = (label.pixelOffset?.getValue ? label.pixelOffset.getValue(currentTime) : label.pixelOffset) || { x: 0, y: 0 }
      const offsetX = isFinite(pixelOffset.x) ? pixelOffset.x : 0
      const offsetY = isFinite(pixelOffset.y) ? pixelOffset.y : 0

      const x = screenPosition.x + offsetX + this.viewportPadding
      const y = screenPosition.y + offsetY + this.viewportPadding

      // 最终验证计算结果
      const box = {
        x1: x - textWidth / 2 - padding,
        y1: y - textHeight - padding,
        x2: x + textWidth / 2 + padding,
        y2: y + padding,
        screenPosition: { x: screenPosition.x, y: screenPosition.y },
        entity: entity
      }

      // 验证边界盒的有效性
      if (!isFinite(box.x1) || !isFinite(box.y1) || !isFinite(box.x2) || !isFinite(box.y2)) {
        return null
      }

      return box
    } catch (error) {
      console.warn('获取标签边界盒失败:', error)
      return null
    }
  }

  // 解析字体大小
  parseFontSize (fontString) {
    // 确保输入是字符串
    if (typeof fontString !== 'string') {
      return 12 // 默认字体大小
    }

    const match = fontString.match(/(\d+)px/)
    return match ? parseInt(match[1]) : 12
  }

  // 获取标签优先级
  getLabelPriority (entity) {
    const layerType = entity.properties?.labelType || 'default'
    return this.labelPriorities[layerType] || this.labelPriorities.default
  }

  // 检测单个标签是否可以放置
  canPlaceLabel (entity) {
    const boundingBox = this.getLabelBoundingBox(entity)
    if (!boundingBox) return false

    // 检查是否在视口内
    if (this.isOffscreen(boundingBox)) return false

    // 检查与已放置标签的碰撞
    return !this.grid.hitTest(boundingBox, entity)
  }

  // 检查是否在屏幕外
  isOffscreen (box) {
    const canvas = this.viewer.scene.canvas
    const screenWidth = canvas.clientWidth + this.viewportPadding
    const screenHeight = canvas.clientHeight + this.viewportPadding

    return box.x2 < this.viewportPadding ||
      box.x1 >= screenWidth ||
      box.y2 < this.viewportPadding ||
      box.y1 >= screenHeight
  }

  // 放置标签
  placeLabel (entity) {
    const boundingBox = this.getLabelBoundingBox(entity)
    if (!boundingBox) return false

    this.grid.insert(boundingBox, entity)
    this.placedLabels.set(entity.id, {
      entity: entity,
      boundingBox: boundingBox,
      priority: this.getLabelPriority(entity)
    })

    return true
  }

  // 异步批量处理标签碰撞检测
  async processLabelsAsync (entities) {
    const startTime = performance.now()

    if (!this.grid) {
      this.initializeGrid()
    } else {
      this.grid.clear()
      this.placedLabels.clear()
    }

    // 限制处理数量以提高性能
    const maxLabels = 800 // 增加处理数量
    const limitedEntities = entities.slice(0, maxLabels)

    // 预过滤：只处理可见的标签
    const visibleEntities = limitedEntities.filter(entity => {
      if (!entity.position || !entity.label) return false

      try {
        // 验证位置
        let position = entity.position
        if (typeof position.getValue === 'function') {
          // 使用当前时间获取Property的值
          const currentTime = this.viewer.clock.currentTime
          position = position.getValue(currentTime)
        }

        if (!position) {
          return false
        }

        // 如果是Cartesian3对象，检查坐标
        if (position.x !== undefined && position.y !== undefined && position.z !== undefined) {
          if (!isFinite(position.x) || !isFinite(position.y) || !isFinite(position.z)) {
            return false
          }
        }

        // 快速检查是否在视野内
        const screenPos = Cesium.SceneTransforms.worldToWindowCoordinates(
          this.viewer.scene,
          position
        )

        if (!screenPos || !isFinite(screenPos.x) || !isFinite(screenPos.y)) return false

        const canvas = this.viewer.scene.canvas
        return screenPos.x >= -100 && screenPos.x <= canvas.clientWidth + 100 &&
          screenPos.y >= -100 && screenPos.y <= canvas.clientHeight + 100
      } catch (error) {
        return false
      }
    })

    // 按优先级排序 (优先级数字越小越重要)
    const sortedEntities = visibleEntities.sort((a, b) => {
      const priorityA = this.getLabelPriority(a)
      const priorityB = this.getLabelPriority(b)

      if (priorityA !== priorityB) {
        return priorityA - priorityB
      }

      // 同优先级的按字体大小排序，大字体优先
      const currentTime = this.viewer.clock.currentTime
      const fontSizeA = this.parseFontSize((a.label?.font?.getValue ? a.label.font.getValue(currentTime) : a.label?.font) || '12px Arial')
      const fontSizeB = this.parseFontSize((b.label?.font?.getValue ? b.label.font.getValue(currentTime) : b.label?.font) || '12px Arial')
      return fontSizeB - fontSizeA
    })

    const results = {
      visible: [],
      hidden: [],
      stats: {
        total: entities.length,
        processed: 0,
        collisions: 0,
        filtered: visibleEntities.length,
        processingTime: 0
      }
    }

    // 异步批量处理标签
    const batchSize = 25 // 减少批次大小，提高响应性
    const totalEntities = sortedEntities.length

    console.log(`开始异步碰撞检测，共 ${totalEntities} 个实体，每批 ${batchSize} 个`)

    for (let i = 0; i < totalEntities; i += batchSize) {
      const endIndex = Math.min(i + batchSize, totalEntities)
      const batch = sortedEntities.slice(i, endIndex)

      // 异步处理当前批次
      await new Promise(resolve => {
        const processBatch = (deadline) => {
          const batchStartTime = performance.now()

          for (const entity of batch) {
            // 检查是否需要让出主线程
            if (deadline && deadline.timeRemaining() < 1) {
              // 时间不够，延迟到下一个空闲时间
              requestIdleCallback(processBatch)
              return
            }

            // 检查批次处理时间，避免单个批次处理过久
            if (performance.now() - batchStartTime > 12) { // 12ms的时间切片
              // 延迟到下一个时间片
              setTimeout(() => processBatch(deadline), 0)
              return
            }

            try {
              results.stats.processed++

              if (this.canPlaceLabel(entity)) {
                this.placeLabel(entity)
                entity.label.show = true
                results.visible.push(entity)
              } else {
                entity.label.show = false
                results.hidden.push(entity)
                results.stats.collisions++
              }
            } catch (error) {
              console.warn(`处理实体碰撞检测时出错:`, error)
              entity.label.show = false
              results.hidden.push(entity)
            }
          }

          resolve()
        }

        // 优先使用 requestIdleCallback，如果不支持则使用 setTimeout
        if (typeof requestIdleCallback !== 'undefined') {
          requestIdleCallback(processBatch)
        } else {
          setTimeout(() => processBatch(), 0)
        }
      })

      // 报告进度
      const progress = (endIndex / totalEntities) * 100
      if (i % (batchSize * 4) === 0) { // 减少进度输出频率
        console.log(`碰撞检测进度: ${progress.toFixed(1)}% (${endIndex}/${totalEntities})`)
      }

      // 每处理几批后让出主线程，避免长时间阻塞
      if ((i / batchSize) % 3 === 0) {
        await new Promise(resolve => setTimeout(resolve, 0))
      }

      // 检查总处理时间，避免阻塞太久
      const currentTime = performance.now()
      if (currentTime - startTime > 150) { // 限制150ms
        console.warn('异步碰撞检测时间过长，提前终止处理')
        break
      }
    }

    // 隐藏未处理的标签
    for (let i = results.stats.processed; i < entities.length; i++) {
      if (entities[i].label) {
        entities[i].label.show = false
      }
    }

    results.stats.processingTime = performance.now() - startTime
    console.log(`异步碰撞检测完成: 可见${results.visible.length}, 隐藏${results.hidden.length}, 用时${results.stats.processingTime.toFixed(2)}ms`)

    return results
  }

  // 批量处理标签碰撞检测（保留原有同步版本）
  processLabels (entities) {
    const startTime = performance.now()

    if (!this.grid) {
      this.initializeGrid()
    } else {
      this.grid.clear()
      this.placedLabels.clear()
    }

    // 限制处理数量以提高性能
    const maxLabels = 800 // 增加处理数量
    const limitedEntities = entities.slice(0, maxLabels)

    // 预过滤：只处理可见的标签
    const visibleEntities = limitedEntities.filter(entity => {
      if (!entity.position || !entity.label) return false

      try {
        // 验证位置
        let position = entity.position
        if (typeof position.getValue === 'function') {
          // 使用当前时间获取Property的值
          const currentTime = this.viewer.clock.currentTime
          position = position.getValue(currentTime)
        }

        if (!position) {
          return false
        }

        // 如果是Cartesian3对象，检查坐标
        if (position.x !== undefined && position.y !== undefined && position.z !== undefined) {
          if (!isFinite(position.x) || !isFinite(position.y) || !isFinite(position.z)) {
            return false
          }
        }

        // 快速检查是否在视野内
        const screenPos = Cesium.SceneTransforms.worldToWindowCoordinates(
          this.viewer.scene,
          position
        )

        if (!screenPos || !isFinite(screenPos.x) || !isFinite(screenPos.y)) return false

        const canvas = this.viewer.scene.canvas
        return screenPos.x >= -100 && screenPos.x <= canvas.clientWidth + 100 &&
          screenPos.y >= -100 && screenPos.y <= canvas.clientHeight + 100
      } catch (error) {
        return false
      }
    })

    // 按优先级排序 (优先级数字越小越重要)
    const sortedEntities = visibleEntities.sort((a, b) => {
      const priorityA = this.getLabelPriority(a)
      const priorityB = this.getLabelPriority(b)

      if (priorityA !== priorityB) {
        return priorityA - priorityB
      }

      // 同优先级的按字体大小排序，大字体优先
      const currentTime = this.viewer.clock.currentTime
      const fontSizeA = this.parseFontSize((a.label?.font?.getValue ? a.label.font.getValue(currentTime) : a.label?.font) || '12px Arial')
      const fontSizeB = this.parseFontSize((b.label?.font?.getValue ? b.label.font.getValue(currentTime) : b.label?.font) || '12px Arial')
      return fontSizeB - fontSizeA
    })

    const results = {
      visible: [],
      hidden: [],
      stats: {
        total: entities.length,
        processed: 0,
        collisions: 0,
        filtered: visibleEntities.length,
        processingTime: 0
      }
    }

    // 批量处理标签
    const batchSize = 50
    for (let i = 0; i < sortedEntities.length; i += batchSize) {
      const batch = sortedEntities.slice(i, i + batchSize)

      for (const entity of batch) {
        results.stats.processed++

        if (this.canPlaceLabel(entity)) {
          this.placeLabel(entity)
          entity.label.show = true
          results.visible.push(entity)
        } else {
          entity.label.show = false
          results.hidden.push(entity)
          results.stats.collisions++
        }
      }

      // 每批处理后检查时间，避免阻塞太久
      const currentTime = performance.now()
      if (currentTime - startTime > 100) { // 限制100ms
        console.warn('碰撞检测时间过长，提前终止处理')
        break
      }
    }

    // 隐藏未处理的标签
    for (let i = results.stats.processed; i < entities.length; i++) {
      if (entities[i].label) {
        entities[i].label.show = false
      }
    }

    results.stats.processingTime = performance.now() - startTime
    return results
  }

  // 动态更新 (相机移动时调用)
  updateCollisionDetection (entities) {
    // 重新计算所有标签的碰撞
    return this.processLabels(entities)
  }

  // 获取碰撞统计信息
  getStats () {
    return {
      gridCells: this.grid ? this.grid.cols * this.grid.rows : 0,
      placedLabels: this.placedLabels.size,
      gridMemoryUsage: this.grid ? this.grid.grid.reduce((sum, cell) => sum + cell.length, 0) : 0
    }
  }
}

export { POICollisionDetector, CollisionGrid } 